package com.kymatrix.kycarbon.xsmd.common.util;


import com.auth0.jwt.JWT;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.jackson.io.JacksonDeserializer;
import io.jsonwebtoken.jackson.io.JacksonSerializer;
import java.time.Duration;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class JwtUtil {

    private static Duration expiration;
    private static Duration autoLoginExpiration;

    private static String jwtId;

    private static String jwtSecret;

    private static final ObjectMapper jsonMapper = JsonMapper.builder().build();

    private static final JacksonSerializer<Map<String, ?>> jacksonSerializer = new JacksonSerializer<>(jsonMapper);

    private static final JacksonDeserializer<Map<String, ?>> jacksonDeserializer 
            = new JacksonDeserializer<>(jsonMapper);

    private static String userKey = "username";
    private static String tenantIdKey = "tenantId";


    /**
     * 获取用户名
     */
    public static String getUsername(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getClaim(userKey).asString();
        } catch (JWTDecodeException e) {
            return null;
        }
    }
    
    /**
     * 创建JWT
     */
    public static String createJwt(Map<String, Object> claims, Duration expiration) {

        long nowMillis = System.currentTimeMillis();
        Date nowDate = new Date(nowMillis);

        SecretKey secretKey = generalKey();

        //下面就是在为payload添加各种标准声明和私有声明了
        //这里其实就是new一个JwtBuilder，设置jwt的body
        JwtBuilder builder = Jwts.builder()
                //如果有私有声明，一定要先设置这个自己创建的私有的声明，这个是给builder的claim赋值，一旦写在标准的声明赋值之后，就是覆盖了那些标准的声明的
                .setClaims(claims)
                //设置jti(JWT ID)：是JWT的唯一标识，根据业务需要，这个可以设置为一个不重复的值，主要用来作为一次性token,从而回避重放攻击。
                .setId(jwtId)
                //iat: jwt的签发时间
                .setIssuedAt(nowDate)
                //设置签名使用的签名算法和签名使用的秘钥
                .signWith(secretKey)
                .serializeToJsonWith(jacksonSerializer);
        if (!expiration.isNegative()) {
            Date exp = Date.from(nowDate.toInstant().plus(expiration));
            //设置过期时间
            builder.setExpiration(exp);
        }
        return builder.compact();
    }


    /**
     * 验证jwt
     */
    public static Claims verifyJwt(String token) {
        //签名秘钥，和生成的签名的秘钥一模一样
        SecretKey key = generalKey();
        Claims claims;
        try {
            //得到DefaultJwtParser
            claims = Jwts.parserBuilder()
                    //设置签名的秘钥
                    .setSigningKey(key)
                    .deserializeJsonWith(jacksonDeserializer)
                    .build()
                    .parseClaimsJws(token).getBody();
        } catch (Exception e) {
            e.printStackTrace();
            claims = null;
        } //设置需要解析的jwt
        return claims;
    }

    /**
     * 由字符串生成加密key
     *
     * @return SecretKey
     */
    public static SecretKey generalKey() {
        String stringKey = jwtSecret;
        byte[] encodedKey = Base64.getDecoder().decode(stringKey);
        return new SecretKeySpec(encodedKey, 0, encodedKey.length, "HmacSHA256");
    }




    /**
     * 根据userName生成token
     */
    public static String generateToken(String username) {
        Map<String, Object> map = new HashMap<>();
        map.put(userKey, username);
        return createJwt(map, expiration);
    }

    /**
     * 根据userName生成token
     */
    public static String generateToken(String username, String tenantId,  Duration expiration) {
        Map<String, Object> map = new HashMap<>();
        map.put(userKey, username);
        map.put(tenantIdKey, tenantId);
        return createJwt(map, expiration);
    }

    @Value("${jwt.expiration}")
    public void setTokenExpiredTime(Duration tokenExpiredTime) {
        setStaticTokenExpiredTime(tokenExpiredTime);

    }

    private static void setStaticTokenExpiredTime(Duration tokenExpiredTime) {
        JwtUtil.expiration = tokenExpiredTime;
    }

    @Value("${jwt.autoLoginExpiration}")
    public void setTokenAutoLoginExpirationTime(Duration autoLoginExpiration) {
        setStaticTokenAutoLoginExpirationTime(autoLoginExpiration);
    }

    private static void setStaticTokenAutoLoginExpirationTime(Duration autoLoginExpiration) {
        JwtUtil.autoLoginExpiration = autoLoginExpiration;
    }

    @Value("${jwt.id}")
    public void setJwtId(String jwtId) {
        setStaticJwtId(jwtId);
    }

    private static void setStaticJwtId(String jwtId) {
        JwtUtil.jwtId = jwtId;
    }

    @Value("${jwt.secret}")
    public void setJwtSecret(String jwtSecret) {
        setStaticJwtSecret(jwtSecret);
    }

    private static void setStaticJwtSecret(String jwtSecret) {
        JwtUtil.jwtSecret = jwtSecret;
    }

    public static Duration getExpiration() {
        return expiration;
    }

    public static Duration getAutoLoginExpiration() {
        return autoLoginExpiration;
    }

    public static String getJwtId() {
        return jwtId;
    }

    public static String getJwtSecret() {
        return jwtSecret;
    }
}
